#include "routeplanning.h"
#include "function.h"
#include "sqlconnectionpool.h"

/******************************
 * 路径选择实现文件
 *
 * 2021/3/12  1.0版
 * 2021/3/15  2.0版
 * 2.21/3/20  3.0版
 * 2021/3/25  4.0版
 *  金雨展
 * *******************************
 */



/********全局变量*****/
ZZ dist[80];
/***************/

RoutePlanning::RoutePlanning()
{

}


bool RoutePlanning:: a_star( int waynum,QString start, QString end,int type)
/****************************************************************
 * Function:        RoutePlanning::a_star
 * Description:         A*算法主函数
 * Calls(被本函数调用的函数):        find，getH，
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:      from ： 起始站点  to ：目的站点
 * Output:    测试信息
 * Return:   是否找到路径
 * Others(其他说明，比如测试建议):   测试各种站点选择；
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/15      完成版本，并验证了一些测试样例
 *                                      修改BUG：
 *
 * ****************************************************************
*/
{
    //检查不存在的点，不存在这个判断
    //if(start < 0 || end < 0 || start > 19 || end > 19) return false;
    NN start_node(0,getH(start,end,end),start,start,0,0);
    openLists.push_back(start_node);
    NN center_node;
    while(1) {
        //第一步，查找openList，找到f最小的点
        //排个序

        std::sort(openLists.begin(), openLists.end(), cmp);
        //接下来需要围绕着当前节点寻找最小f的点
        //并且将它删除
        center_node = *openLists.begin();
        openLists.erase(openLists.begin());
        closeLists.push_back(center_node);
        QString temp_start = center_node.id;
        //................................
        //将一个节点的所有的可到达的领域加入到openLists中

        if (type == 1){
            for(int i = 0; i <waynum; ++ i) {//遍历每一条路

                //如果这条路的开始节点是当前节点，并且这条路的到达节点不在close中，
                //也不在open中，就可以加入了dist[temp_start][i]
                if(dist[i].a == temp_start && !node_in(dist[i].b,closeLists) && !node_in(dist[i].b,openLists)){
                    double cost = center_node.cost + dist[i].timeCost;//计算时间费用
                    double fee = center_node.fee + dist[i].fee;//计算费用
                    NN new_node(dist[i].timeCost+center_node.g, getH(dist[i].a,dist[i].b,end), dist[i].b, center_node.id,cost,fee);
                    openLists.push_back(new_node);
                }//如果在close中,close中应该删掉他.判断是否要加入open中***************************************
                else if(dist[i].a == temp_start && node_in(dist[i].b,closeLists) && !node_in(dist[i].b,openLists)){
                    NN node = findNode_byId(openLists, dist[i].b);
                    //如果从当前点更优的话，那么就改变G值，然后将这个node的father变成当前的点
                    //并且重新排openList的顺序
                    if(center_node.g + dist[i].timeCost < node.g) {
                        node.g = center_node.g + dist[i].timeCost;
                        node.father_id = center_node.id;
                        node.f = node.g + node.h;
                        node.cost = center_node.cost + dist[i].fee;//增加费用
                         node.fee = center_node.cost + dist[i].timeCost;//记录时间花费
                        std::sort(openLists.begin(), openLists.end(), cmp);
                        }
                    }
                else{
                        continue;
                    }

            //终点在open里面就可以结束了
            if(node_in(end, openLists)) return true;
            if(openLists.empty()) return false;
          }
        }
        else if(type == 2){
            for(int i = 0; i <waynum; ++ i) {//遍历每一条路

                //如果这条路的开始节点是当前节点，并且这条路的到达节点不在close中，
                //也不在open中，就可以加入了dist[temp_start][i]
                if(dist[i].a == temp_start && !node_in(dist[i].b,closeLists) && !node_in(dist[i].b,openLists)){
                    double cost = center_node.cost + dist[i].timeCost;//计算时间费用
                    double fee = center_node.fee + dist[i].fee;//计算费用
                    NN new_node(dist[i].fee+center_node.g, getH(dist[i].a,dist[i].b,end), dist[i].b, center_node.id,cost,fee);
                    openLists.push_back(new_node);
                }//如果在close中,close中应该删掉他.判断是否要加入open中***************************************
                else if(dist[i].a == temp_start && node_in(dist[i].b,closeLists) && !node_in(dist[i].b,openLists)){
                    NN node = findNode_byId(openLists, dist[i].b);
                    //如果从当前点更优的话，那么就改变G值，然后将这个node的father变成当前的点
                    //并且重新排openList的顺序
                    if(center_node.g + dist[i].fee < node.g) {
                        node.g = center_node.g + dist[i].fee;
                        node.father_id = center_node.id;
                        node.f = node.g + node.h;
                        node.cost = center_node.cost + dist[i].timeCost;//增加费用
                         node.fee = center_node.cost + dist[i].timeCost;//记录时间花费
                        std::sort(openLists.begin(), openLists.end(), cmp);
                        }
                    }
                else{
                        continue;
                    }

            //终点在open里面就可以结束了
            if(node_in(end, openLists)) return true;
            if(openLists.empty()) return false;
          }
        }
        else{
            for(int i = 0; i <waynum; ++ i) {//遍历每一条路

                //如果这条路的开始节点是当前节点，并且这条路的到达节点不在close中，
                //也不在open中，就可以加入了dist[temp_start][i]
                if(dist[i].a == temp_start && !node_in(dist[i].b,closeLists) && !node_in(dist[i].b,openLists)){
                    double cost = center_node.cost + dist[i].timeCost;//计算时间费用
                    double fee = center_node.fee + dist[i].fee;//计算费用
                    NN new_node(dist[i].fee / dist[i].timeCost + center_node.g, getH(dist[i].a,dist[i].b,end), dist[i].b, center_node.id,cost,fee);
                    openLists.push_back(new_node);
                }//如果在close中,close中应该删掉他.判断是否要加入open中***************************************
                else if(dist[i].a == temp_start && node_in(dist[i].b,closeLists) && !node_in(dist[i].b,openLists)){
                    NN node = findNode_byId(openLists, dist[i].b);
                    //如果从当前点更优的话，那么就改变G值，然后将这个node的father变成当前的点
                    //并且重新排openList的顺序
                    if(center_node.g + dist[i].fee / dist[i].timeCost < node.g) {
                        node.g = center_node.g + dist[i].fee / dist[i].timeCost;
                        node.father_id = center_node.id;
                        node.f = node.g + node.h;
                        node.cost = center_node.cost + dist[i].fee;//增加费用
                        node.fee = center_node.cost + dist[i].timeCost;//记录时间花费
                        std::sort(openLists.begin(), openLists.end(), cmp);
                        }
                    }
                else{
                        continue;
                    }

            //终点在open里面就可以结束了
            if(node_in(end, openLists)) return true;
            if(openLists.empty()) return false;
          }
        }
   }
}


//不用改
int RoutePlanning::readDist()
/****************************************************************
 * Function:        RoutePlanning::readDist
 * Description:         读取城市名称及线路的相关信息
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    测试信息
 * Return:   现有城市个数
 * Others(其他说明，比如测试建议):   测试各种站点选择；
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/15      完成版本，并验证了一些测试样例
 *                                      修改BUG：
 *
 * ****************************************************************
*/
{
    QSqlDatabase db = SqlConnectionPool::openConnection();


    //查询城市名字******************************************************************************************
    QString rowname = "siteId";
    QString tablename = "StationTable";
    QSqlQuery result = db.exec(" select " + rowname + " from " + tablename + ";");
    int i=0;
        while(result.next()){
            //qDebug()<<"siteId:"<<result.value("siteId");
            city << result.value("siteId").toString();
            qDebug()<<result.value("siteId");
            i++;
        }

    //查询路线,先弄时间的，线路一线路二，代价

        rowname = "siteId,toSite,timeCost,fee";
        tablename = "LineTable";
        result = db.exec(" select " + rowname + " from " + tablename + ";");
        i=0;
            while(result.next()){
                dist[i].a = result.value("siteId").toString();
                dist[i].b = result.value("toSite").toString();
                dist[i].timeCost = result.value("timeCost").toDouble();
                dist[i].fee = result.value("fee").toDouble();
                qDebug()<<"siteId:"<<result.value("siteId").toInt();
                qDebug()<<"toSite:"<<result.value("toSite").toString();
                qDebug()<<"timeCost:"<<result.value("timeCost").toInt();
                qDebug()<<"fee:"<<result.value("fee").toDouble();
                i++;
                //线路存两遍
                dist[i].b = result.value("siteId").toString();
                dist[i].a = result.value("toSite").toString();
                dist[i].timeCost = result.value("timeCost").toDouble();
                dist[i].fee = result.value("fee").toDouble();
                i++;
            }
            SqlConnectionPool::closeConnection(db);
            return i;


}






//**********************************************************增加站点用***********************************

double RoutePlanning::strai_dist(QString jingguo, double x2,double y2)
/****************************************************************
 * Function:        RoutePlanning::strai_dist
 * Description:         读取城市名称及线路的相关信息求某一城市到给定点的距离（增加站点使用版本）
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  StationTable
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    测试信息
 * Return:   两点间距离
 *
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/15      完成版本，并验证了一些测试样例
 *                                      修改BUG：
 *
 * ****************************************************************
*/
{
    //这个求每个点到终点的距离/代价
    //要经过的点


    QSqlDatabase db = SqlConnectionPool::openConnection();
    SqlConnectionPool::closeConnection(db);


    //QString rowname = "siteId,coordinateX,coordinateY";
    QString tablename = "StationTable";

    qDebug()<<"here *********************************************************************************";
    QSqlQuery result = db.exec(" select * from " + tablename);

    double x1, y1;
    while(result.next()){
        if(result.value("siteId").toString() == jingguo){  //在这里放你要查的id
            //在这里放你要做的操作
            x1 = result.value("coordinateX").toDouble();
            y1 = result.value("coordinateY").toDouble();
            qDebug()<<"coordinateX1 "<<result.value("coordinateX").toDouble() << " coordinateY1 "<<result.value("coordinateY").toDouble();


        }
    }

    result.clear();


    //求目标点的坐标
    qDebug()<<"distance:"<<sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    // rowname = "coordinateX,coordinateY”

    //qDebug()<<"X1 "<<x1 << " y1 "<<y1;
    //qDebug()<<"X2 "<<x2 << " y2 "<<y2;
    SqlConnectionPool::closeConnection(db);
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));

}
//**********************************************************************************


//不用改
double RoutePlanning::strai_dist(QString jingguo, QString mubiao)
/****************************************************************
 * Function:        RoutePlanning::strai_dist
 * Description:         求两点间距离（A*使用版本）
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  StationTable
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    测试信息
 * Return:   两点间距离
 *
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/15      完成版本，并验证了一些测试样例
 *                                      修改BUG：
 *
 * ****************************************************************
*/
{
    //这个求每个点到终点的距离/代价
    //要经过的点


    QSqlDatabase db = SqlConnectionPool::openConnection();



    //QString rowname = "siteId,coordinateX,coordinateY";
    QString tablename = "StationTable";

    qDebug()<<"here *********************************************************************************";
    QSqlQuery result = db.exec(" select * from " + tablename);

    double x1, y1, x2, y2;
    while(result.next()){
        if(result.value("siteId").toString() == jingguo){  //在这里放你要查的id
            //在这里放你要做的操作
            x1 = result.value("coordinateX").toDouble();
            y1 = result.value("coordinateY").toDouble();
            qDebug()<<"coordinateX1 "<<result.value("coordinateX").toDouble() << " coordinateY1 "<<result.value("coordinateY").toDouble();


        }
    }

    result.clear();

    result = db.exec(" select * from " + tablename);
    while(result.next()){
        if(result.value("siteId").toString() == mubiao){  //在这里放你要查的id
            //在这里放你要做的操作
            x2 = result.value("coordinateX").toDouble();
            y2 = result.value("coordinateY").toDouble();

            qDebug()<<"coordinateX2 "<<result.value("coordinateX").toDouble() << " coordinateY2 "<<result.value("coordinateY").toDouble();

        }
    }
    //求目标点的坐标
    qDebug()<<"distance:"<<sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
    // rowname = "coordinateX,coordinateY”

    //qDebug()<<"X1 "<<x1 << " y1 "<<y1;
    //qDebug()<<"X2 "<<x2 << " y2 "<<y2;
    SqlConnectionPool::closeConnection(db);
    return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));

}
//不用改
double RoutePlanning::getH(QString jingguo1, QString jingguo2,QString mubiao)
/****************************************************************
 * Function:        RoutePlanning::getH
 * Description:         给出两点到目标点距离的差
 * Calls(被本函数调用的函数):        strai_dist
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    测试信息
 * Return:   两点到目标点距离的差
 *
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/16      完成版本，并验证了一些测试样例
 *                                      修改BUG：
 *
 * ****************************************************************
*/
{

    return abs(strai_dist(jingguo1,mubiao)- strai_dist(jingguo2,mubiao));
}
//不用改
bool RoutePlanning::cmp(const NN &node1, const NN &node2)
/****************************************************************
 * Function:        RoutePlanning::cmp
 * Description:         sort排序函数使用
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    无
 * Return:   bool值
 * test:  该函数无需测试
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/14      完成版本，并验证了一些测试样例
 *
 * ****************************************************************
*/
{
    if(node1.f < node2.f) return true;
    else if(node1.f > node2.f) return false;
    else {
        if(node1.g < node2.g) return true;
    }
    return false;
}

//不用改
bool RoutePlanning::node_in(QString id, QVector<NN> List)
/****************************************************************
 * Function:        RoutePlanning::node_in
 * Description:         判断List中是否有某一点
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    无
 * Return:   bool值
 * test:  该函数无需测试
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/14      完成版本，并验证了一些测试样例
 *
 * ****************************************************************
*/
{
    for(auto it = List.begin(); it != List.end(); ++ it) {
        if(it->id == id) return true;
    }
    return false;
}

//不用改
NN RoutePlanning::findNode_byId(QVector<NN> list, QString id)
/****************************************************************
 * Function:        RoutePlanning::findNode_byId
 * Description:         findNode_byId
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:
 * Output:    无
 * Return:   NN
 * test:  该函数无需测试
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/14      完成版本，并验证了一些测试样例
 *
 * ****************************************************************
*/
{
    for(int i = 0; i < list.size(); ++ i) {
        if(id == list[i].id) return list[i];
    }
    return NN(-1,-1,"","",0,0);
}


//时间花费的，记录距离
QList<QString> RoutePlanning::find(QString start,QString end,int type)
/****************************************************************
 * Function:        RoutePlanning::find
 * Description:         findNode_byId
 * Calls(被本函数调用的函数):        无
 * Table Accessed(被访问的数据库中的表):  无
 * Table Updated(被修改的数据库中的表):   无
 * Input:      start ： 起始点  end ： 终点  type ： 路线选择类型
 * Output:    测试信息
 * Return:   QList 费用  价格 运单号
 * test:  调用测试
 *History(历史修改记录)：
 * <作者>   <时间>  <改动>
 * 金雨展     2021/3/18      完成1.0版本，并验证了一些测试样例
 *           2021/3/122     完成2.0版本，并验证了一些测试样例
 * ****************************************************************
*/
{//1、时间最优  3、性价比最优
        int waynum = readDist();//把城市以及线路都读入了
       // int start,end;
        QList<QString> row;
        QString way = "";
        QString timecost = "";
        QString  feecost = "";
        if(a_star( waynum,start, end,type)) {
            NN node = findNode_byId(openLists, end);
            qDebug()<<"最短路线长度为: "<<node.f;
            timecost = timecost + QString::number(node.cost, 10, 3);//记录时间花费
            feecost = feecost + QString::number(node.fee, 10, 3);//记录费用
            std::stack<QString> track;
            track.push(node.id);
            way = way + node.id;
            while(node.id != start) {
                node = findNode_byId(closeLists, node.father_id);
                track.push(node.id);
                way = way + node.id;
            }

            qDebug()<<"行进路线应为: ";
            qDebug()<<track.top();
            track.pop();
            while(!track.empty()) {
                qDebug()<<" -> "<<track.top();
                track.pop();
            }
             qDebug();
        }
        else {
             qDebug()<<"search failed!";
        }
        qDebug()<<timecost<<feecost<<way;
        row<<timecost<<feecost<<way;
        return row;
}





